package ge.telasi.tasks.controller;

import ge.telasi.tasks.model.TaskAttachment;
import ge.telasi.tasks.model.Group;
import ge.telasi.tasks.model.Task;
import ge.telasi.tasks.model.TaskReceiver;
import ge.telasi.tasks.model.TaskRelation;
import ge.telasi.tasks.model.User;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.mail.MessagingException;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;

/**
 * This class is used for sending messages.
 * 
 * @author dimitri
 */
public class NotificationManager extends Controller {

    private void sendMessage(User user, String text) {
        try {
            InitialContext ctxt = new InitialContext();
            QueueConnectionFactory queueConnectionFactory = (QueueConnectionFactory) ctxt.lookup("queueConnectionFactory");
            Queue queue = (Queue) ctxt.lookup("jms/TaskQueue");
            QueueConnection queueConnection = queueConnectionFactory.createQueueConnection();
            try {
                QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
                QueueSender queueSender = queueSession.createSender(queue);
                TextMessage message = queueSession.createTextMessage();
                message.setJMSExpiration(24 * 3600 * 1000); // save for 1 day
                message.setStringProperty("receiver", user.getUsername());
                message.setText(text);
                queueSender.send(message);
            } finally {
                try {queueConnection.close();} catch(Exception ex) {}
            }
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }

    private void sendEmail(EntityManager em, User user, String text, List<TaskAttachment> attachemnts) throws MessagingException {
        new EmailController().sendMessage(em, user, "Telasi Tasks System Notification", text, attachemnts);
    }

    private void sendEmail(EntityManager em, User user, String text) throws MessagingException {
        sendEmail(em, user, text, null);
    }

    private void appendUser(List<User> users, User user, User except) {
        if (users.contains(user)) return;
        if (except != null && except.equals(user)) return;
        users.add(user);
    }

    private void appendUser(List<User> users, Group group, User except) {
        for (User user : group.getUsers()) {
            appendUser(users, user, except);
        }
    }

    private List<User> allUsers(List<TaskReceiver> receivers, User except) {
        List<User> users = new ArrayList<User>();
        for (TaskReceiver receiver : receivers) {
            if (receiver.getUser() != null) {
                appendUser(users, receiver.getUser(), except);
            } else {
                appendUser(users, receiver.getGroup(), except);
            }
        }
        return users;
    }

    /**
     * Default receiver list.
     */
    private List<User> createReceiverList(Task task, User user) {
        // empty task
        if (task == null) return new ArrayList<User>();

        // full list of message receivers
        List<User> receivers = new ArrayList();

        // notify owner if task was prepared by somebody else
        if (task.getOwner()!=null && !task.getOwner().equals(user)) {
            receivers.add(task.getOwner());
        }

        // add receivers, if not a draft
        boolean draft = task.isDraft();
        if (!draft) {
            for (TaskReceiver receiver : task.getReceivers()) {
                if (receiver.getUser() != null) {
                    appendUser(receivers, receiver.getUser(), user);
                } else if (receiver.getGroup() != null) {
                    appendUser(receivers, receiver.getGroup(), user);
                }
            }
        }

        return receivers;
    }

    /**
     * Notifies task creation.
     */
    public void notifyCreation(final EntityManager em, final User user, final Task task) {
        Runnable run = new Runnable() {
            public void run() {
                // list of message receivers
                List<User> receivers = createReceiverList(task, user);

                // create and send message
                String msg = getLocaleManager().getMessage("TaskController.newMessage",
                        task.getNumber(), task.getSubject(),
                        user.getFullName(), user.getUsername());

                StringBuilder emailMsg = new StringBuilder();
                emailMsg.append("თქვენ მოგივიდათ ახალი დავალება!!!");
                emailMsg.append("\n\nნომერი: ");
                emailMsg.append(task.getNumber());
                emailMsg.append("\nსათაური: ");
                emailMsg.append(task.getSubject());
                emailMsg.append("\nვისგან: ");
                emailMsg.append(user.getFullName());
                emailMsg.append("\nვისგან (მომხმარებელი): ");
                emailMsg.append(user.getUsername());
                emailMsg.append("\nტექსტი: ");
                emailMsg.append(task.getLastBody());

                for (User receiver : receivers) {
                    try {
                        sendMessage(receiver, msg);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    try {
                        sendEmail(em, receiver, emailMsg.toString(), task.getAttachments());
                    } catch(Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        };
        new Thread(run).start();
    }

    public void notifySubjectChange(final EntityManager em, final User user, final Task task, final String oldSubject) {
        Runnable run = new Runnable() {
            public void run() {
                // compare subjects
                String newSubject = task.getSubject();
                if (newSubject.equals(oldSubject)) {
                    return;
                }

                StringBuilder emailMsg = new StringBuilder();
                emailMsg.append("დავალებას შეეცვალა სათაური!!!");
                emailMsg.append("\n\nდავალების ნომერი: ");
                emailMsg.append(task.getNumber());
                emailMsg.append("\nძველი სათაური: ");
                emailMsg.append(oldSubject);
                emailMsg.append("\nახალი სათაური: ");
                emailMsg.append(task.getSubject());
                emailMsg.append("\nვინ შეცვალა: ");
                emailMsg.append(user.getFullName());
                emailMsg.append("\nვინ შეცვალა (მომხმარებელი): ");
                emailMsg.append(user.getUsername());

                // list of message receivers
                List<User> receivers = createReceiverList(task, user);

                // create and send message
                String msg = getLocaleManager().getMessage("TaskController.subjectChange",
                        task.getNumber(), oldSubject, task.getSubject(),
                        user.getFullName(), user.getUsername());
                for (User receiver : receivers) {
                    try {
                        sendMessage(receiver, msg);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    try {
                        sendEmail(em, receiver, emailMsg.toString());
                    } catch(Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        };
        new Thread(run).start();
    }

    public void notifyPriorityChange(final EntityManager em, final User user, final Task task, final int oldPriority, final Calendar oldDueDate) {
        Runnable run = new Runnable() {
            public void run() {
                // check whether priority/due date was changed
                int newPriority = task.getPriority();
                Calendar newDueDate = task.getDueDate();
                boolean prioritySame = newPriority == oldPriority;
                boolean dateSame = (oldDueDate == null && newDueDate == null)
                        || (newDueDate != null && newDueDate.equals(oldDueDate));
                if (prioritySame && dateSame) return;

                StringBuilder emailMsg = new StringBuilder();
                emailMsg.append("დავალებას შეეცვალა პრიორიტეტი!!!");
                emailMsg.append("\n\nდავალების ნომერი: ");
                emailMsg.append(task.getNumber());
                emailMsg.append("\nძველი პრიორიტეტი: ");
                emailMsg.append(oldPriority);
                emailMsg.append("\nძველი ბოლო ვადა: ");
                emailMsg.append(oldDueDate == null ? " -- " : oldDueDate.getTime());
                emailMsg.append("\nახალი პრიორიტეტი: ");
                emailMsg.append(task.getPriority());
                emailMsg.append("\nახალი ბოლო ვადა: ");
                emailMsg.append(task.getDueDate() == null ? " -- " : task.getDueDate().getTime());
                emailMsg.append("\nვინ შეცვალა: ");
                emailMsg.append(user.getFullName());
                emailMsg.append("\nვინ შეცვალა (მომხმარებელი): ");
                emailMsg.append(user.getUsername());

                // list of message receivers
                List<User> receivers = createReceiverList(task, user);

                // create and send message
                String msg = getLocaleManager().getMessage("TaskController.priorityChange",
                        task.getNumber(), task.getSubject(), user.getFullName(), user.getUsername());
                for (User receiver : receivers) {
                    try {
                        sendMessage(receiver, msg);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    try {
                        sendEmail(em, receiver, emailMsg.toString());
                    } catch(Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        };
        new Thread(run).start();
    }

    public void notifyReceiverChange(final EntityManager em, final User user, final Task task, final List<TaskReceiver> oldReceivers) {
        Runnable run = new Runnable() {
            public void run() {
                if (!task.isDraft()) {
                    // determine which users was added and which was removed from this task
                    List<User> removedUsers = new ArrayList<User>();
                    List<User> addedUsers = new ArrayList<User>();
                    List<User> oldUsers = allUsers(oldReceivers, user);
                    List<User> newUsers = allUsers(task.getReceivers(), user);
                    for (User oldUser : oldUsers) {
                        if (!newUsers.contains(oldUser)) {
                            removedUsers.add(oldUser);
                        }
                    }
                    for (User newUser : newUsers) {
                        if (!oldUsers.contains(newUser)) {
                            addedUsers.add(newUser);
                        }
                    }

                    StringBuilder removedText = new StringBuilder("თქვენ წაგშალეს დავალებიდან!!!");
                    StringBuilder addedText = new StringBuilder("თქვენ დაგამატეს დავალებაში!!!");
                    StringBuilder common = new StringBuilder();
                    common.append("\n\nდავალების ნომერი: ");
                    common.append(task.getNumber());
                    common.append("\nვინ შეცვალა: ");
                    common.append(user.getFullName());
                    common.append("\nვინ შეცვალა (მომხმარებელი): ");
                    common.append(user.getUsername());
                    removedText.append(common.toString());
                    addedText.append(common.toString());

                    StringBuilder removedUsersText = new StringBuilder();
                    StringBuilder addedUsersText = new StringBuilder();

                    for (User removedUser : removedUsers) {
                        removedUsersText.append(removedUser.getFullName());
                        removedUsersText.append("<");
                        removedUsersText.append(removedUser.getUsername());
                        removedUsersText.append(">; ");
                    }

                    for (User addedUser : addedUsers) {
                        addedUsersText.append(addedUser.getFullName());
                        addedUsersText.append("<");
                        addedUsersText.append(addedUser.getUsername());
                        addedUsersText.append(">; ");
                    }

                    // send remove messages
                    for (User removedUser : removedUsers) {
                        String msg = getLocaleManager().getMessage("TaskController.removeFromTaskReceivers",
                            task.getNumber(), task.getSubject(), user.getFullName(), user.getUsername());
                        try {
                            sendMessage(removedUser, msg);
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                        try {
                            sendEmail(em, removedUser, removedText.toString());
                        } catch(Exception ex) {
                            ex.printStackTrace();
                        }
                    }

                    // send add messages
                    for (User addedUser : addedUsers) {
                        String msg = getLocaleManager().getMessage("TaskController.addToTaskReceivers",
                            task.getNumber(), task.getSubject(), user.getFullName(), user.getUsername());
                        try {
                            sendMessage(addedUser, msg);
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                        try {
                           sendEmail(em, user, addedText.toString());
                        } catch(Exception ex) {
                            ex.printStackTrace();
                        }
                    }

                    // notify owner
//                    if (!user.equals(task.getOwner())) {
                    String msg = getLocaleManager().getMessage("TaskController.receiversChange",
                            task.getNumber(), task.getSubject(), user.getFullName(), user.getUsername());
                    try {
                        sendMessage(user, msg);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    try {
                         StringBuilder b = new StringBuilder();
                        b.append("დავალებაში შეცვლილია შემსრულებლები!!! ");
                        b.append(common.toString());
                        b.append("\nდაემატა: ");
                        b.append(addedUsersText.toString());
                        b.append("\nმოაკლდა: ");
                        b.append(removedUsersText.toString());
                        sendEmail(em, user, b.toString());

                    } catch(Exception ex) {
                        ex.printStackTrace();
                    }
//                    }
                }
            }
        };
        new Thread(run).start();
    }

    public void notifyStatusChange(final EntityManager em, final User user, final Task task) {
        Runnable run = new Runnable() {
            public void run() {
                // get receiver list
                List<User> receivers = createReceiverList(task, user);

                StringBuilder emailMsg = new StringBuilder();
                emailMsg.append("დავალებას დაემატა კომენტარი!!!");
                emailMsg.append("\n\nდავალების ნომერი: ");
                emailMsg.append(task.getNumber());
                emailMsg.append("\nმიმდინარე სტატუსი: ");
                emailMsg.append(Task.getStatusName(getLocaleManager(), task.getTaskStatus()));
                emailMsg.append("\nვინ შეცვალა: ");
                emailMsg.append(user.getFullName());
                emailMsg.append("\nვინ შეცვალა (მომხმარებელი): ");
                emailMsg.append(user.getUsername());
                emailMsg.append("\nბოლო დამატებული კომენტარი: ");
                emailMsg.append(task.getLastBody());

                // create and send message
                String msg = getLocaleManager().getMessage("TaskController.statusChange",
                        task.getNumber(), task.getSubject(), user.getFullName(), user.getUsername());
                for (User receiver : receivers) {
                    try {
                        sendMessage(receiver, msg);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    try {
                       sendEmail(em, receiver, emailMsg.toString());
                    } catch(Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        };
        new Thread(run).start();
    }

    public void notifyAttachmentManipulation(final EntityManager em, final User user, final TaskAttachment bin, final boolean remove) {
        Runnable run = new Runnable() {
            public void run() {
                // task
                Task task = bin.getTask();
                if (task == null) return;

                // get receiver list
                List<User> receivers = createReceiverList(task, user);

                List<TaskAttachment> attachments = new ArrayList<TaskAttachment>();

                // create and send message
                String msg = getLocaleManager().getMessage(
                        remove ? "TaskController.removeAttachment" : "TaskController.addAttachment",
                        task.getNumber(), task.getSubject(), bin.getName(), user.getFullName(), user.getUsername());
                for (User receiver : receivers) {
                    try {
                        sendMessage(receiver, msg);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    try {
                       sendEmail(em, receiver, msg, attachments);
                    } catch(Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        };
        new Thread(run).start();
    }

    public void notifyParentManipulation(EntityManager em, User user, Task task, List<TaskRelation> relations, boolean remove) {
        // get receiver list
        List<User> receivers = createReceiverList(task, user);

        // parent task list
        StringBuilder b = new StringBuilder();
        b.append("\n");
        for (TaskRelation relation : relations) {
            b.append(" * ");
            b.append(relation.getParent().getNumber());
            b.append("\n");
        }
        String parentList = b.toString();

        // create and send message
        String msg = getLocaleManager().getMessage(
                remove ? "TaskController.removeParent" : "TaskController.addParent",
                task.getNumber(), task.getSubject(), parentList, user.getFullName(), user.getUsername());
        for (User receiver : receivers) {
            sendMessage(receiver, msg);
        }
    }

    public void notifyTaskDeletion(EntityManager em, Task task, User user) {
        // get receiver list
        List<User> receivers = createReceiverList(task, user);

        // create and send message
        String msg = getLocaleManager().getMessage("TaskController.deleteTask",
                task.getNumber(), task.getSubject(), user.getFullName(), user.getUsername());
        for (User receiver : receivers) {
            sendMessage(receiver, msg);
        }
    }

    public void notifyNewUserRegistration(EntityManager em, User user) {
        List<User> admins = new UserController().getActiveAdmins(em);
        for (User admin : admins) {
            String msg = getLocaleManager().getMessage("UserController.newUserCreated",
                    user.getFullName(), user.getUsername());
            sendMessage(admin, msg);
        }
    }

}
